package xyz.fivefaith.tool.common.util;

import java.io.IOException;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.nio.file.Path;
import java.time.Duration;
import java.util.Map;
import java.util.concurrent.CompletableFuture;

/**
 * Java 11 HttpClient 轻量级工具类
 *
 * @author zhaoanchi
 * @version 1.0
 * @since 1.0
 */
public final class HttpUtil {

    private static final HttpClient CLIENT =
            HttpClient.newBuilder()
                    .connectTimeout(Duration.ofSeconds(10))
                    .followRedirects(HttpClient.Redirect.NORMAL)
                    .build();

    private HttpUtil() {}

    /* ------------------------- 同步方法 ------------------------- */

    /** GET 请求 */
    public static String get(String url, Map<String, String> headers, Map<String, String> params) {
        HttpRequest request = buildGetRequest(url, headers, params);
        return send(request);
    }

    /** POST JSON */
    public static String postJson(String url, Map<String, String> headers, Object bodyObj) {
        try {
            String body =
                    bodyObj instanceof String ? (String) bodyObj : JacksonUtil.toJson(bodyObj);
            HttpRequest request = buildPostJsonRequest(url, headers, body);
            return send(request);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /** POST Form 表单 */
    public static String postForm(
            String url, Map<String, String> headers, Map<String, String> form) {
        HttpRequest request = buildPostFormRequest(url, headers, form);
        return send(request);
    }

    /** 下载文件 */
    public static Path download(String url, Path target) {
        HttpRequest request = HttpRequest.newBuilder(URI.create(url)).GET().build();
        try {
            return CLIENT.send(request, HttpResponse.BodyHandlers.ofFile(target)).body();
        } catch (IOException | InterruptedException e) {
            throw new RuntimeException(e);
        }
    }

    /* ------------------------- 异步方法 ------------------------- */

    public static CompletableFuture<String> getAsync(
            String url, Map<String, String> headers, Map<String, String> params) {
        HttpRequest request = buildGetRequest(url, headers, params);
        return sendAsync(request);
    }

    public static CompletableFuture<String> postJsonAsync(
            String url, Map<String, String> headers, Object bodyObj) {
        try {
            String body =
                    bodyObj instanceof String ? (String) bodyObj : JacksonUtil.toJson(bodyObj);
            HttpRequest request = buildPostJsonRequest(url, headers, body);
            return sendAsync(request);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /* ------------------------- 内部构造 ------------------------- */

    private static HttpRequest buildGetRequest(
            String url, Map<String, String> headers, Map<String, String> params) {
        String finalUrl = appendParams(url, params);
        HttpRequest.Builder builder = HttpRequest.newBuilder(URI.create(finalUrl)).GET();
        addHeaders(builder, headers);
        return builder.timeout(Duration.ofSeconds(10)).build();
    }

    private static HttpRequest buildPostJsonRequest(
            String url, Map<String, String> headers, String body) {
        HttpRequest.Builder builder =
                HttpRequest.newBuilder(URI.create(url))
                        .POST(
                                HttpRequest.BodyPublishers.ofString(
                                        body, java.nio.charset.StandardCharsets.UTF_8))
                        .header("Content-Type", "application/json;charset=utf-8");
        addHeaders(builder, headers);
        return builder.timeout(Duration.ofSeconds(10)).build();
    }

    private static HttpRequest buildPostFormRequest(
            String url, Map<String, String> headers, Map<String, String> form) {
        String formBody =
                form.entrySet().stream()
                        .map(
                                e ->
                                        e.getKey()
                                                + "="
                                                + java.net.URLEncoder.encode(
                                                        e.getValue(),
                                                        java.nio.charset.StandardCharsets.UTF_8))
                        .reduce((a, b) -> a + "&" + b)
                        .orElse("");
        HttpRequest.Builder builder =
                HttpRequest.newBuilder(URI.create(url))
                        .POST(
                                HttpRequest.BodyPublishers.ofString(
                                        formBody, java.nio.charset.StandardCharsets.UTF_8))
                        .header("Content-Type", "application/x-www-form-urlencoded;charset=utf-8");
        addHeaders(builder, headers);
        return builder.timeout(Duration.ofSeconds(10)).build();
    }

    private static void addHeaders(HttpRequest.Builder builder, Map<String, String> headers) {
        if (headers != null) {
            headers.forEach(builder::header);
        }
    }

    private static String appendParams(String url, Map<String, String> params) {
        if (params == null || params.isEmpty()) {
            return url;
        }
        String tail =
                params.entrySet().stream()
                        .map(
                                e ->
                                        e.getKey()
                                                + "="
                                                + java.net.URLEncoder.encode(
                                                        e.getValue(),
                                                        java.nio.charset.StandardCharsets.UTF_8))
                        .reduce((a, b) -> a + "&" + b)
                        .orElse("");
        return url + (url.contains("?") ? "&" : "?") + tail;
    }

    private static String send(HttpRequest request) {
        try {
            HttpResponse<String> resp =
                    CLIENT.send(
                            request,
                            HttpResponse.BodyHandlers.ofString(
                                    java.nio.charset.StandardCharsets.UTF_8));
            if (resp.statusCode() >= 200 && resp.statusCode() < 300) {
                return resp.body();
            }
            throw new RuntimeException(
                    "Http status " + resp.statusCode() + ", body=" + resp.body());
        } catch (IOException | InterruptedException e) {
            throw new RuntimeException(e);
        }
    }

    private static CompletableFuture<String> sendAsync(HttpRequest request) {
        return CLIENT.sendAsync(
                        request,
                        HttpResponse.BodyHandlers.ofString(java.nio.charset.StandardCharsets.UTF_8))
                .thenApply(
                        resp -> {
                            if (resp.statusCode() >= 200 && resp.statusCode() < 300) {
                                return resp.body();
                            }
                            throw new RuntimeException(
                                    "Http status " + resp.statusCode() + ", body=" + resp.body());
                        });
    }
}
